package com.xlabs.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;
import com.xlabs.client.Elements.TextLink;
import com.xlabs.client.DTO.ReplyDTO;
import com.xlabs.client.DTO.QuestionDTO;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Conversation view class.
 */
public class Conversation extends Composite {
    /**
     * Conversation Tree.
     */
    private ScrollPanel treeScroll = new ScrollPanel();

    /**
     * ConversationPanel.
     */
    private HorizontalPanel conversationPanel = new HorizontalPanel();

    /**
     * Conversation scroll panel.
     */
    private ScrollPanel conversationScroll = new ScrollPanel();

    /**
     * Corresponding question encoded key.
     */
    private QuestionDTO questionDTO;

    /**
     * Initializes Conversation.
     */
    public Conversation()
    {
        this.initWidget(this.conversationPanel);
        
        this.setSize("750", "300");

        this.conversationPanel.add(this.conversationScroll);
        this.conversationPanel.add(this.treeScroll);

        this.treeScroll.setSize("250", "300");
        this.conversationScroll.setSize("500", "300");

        this.treeScroll.addStyleName("ScrollPanel");
        this.conversationScroll.addStyleName("ScrollPanel");
    }

    /**
     * Gets {@link #questionDTO}
     * @return {@link #questionDTO}
     */
    public QuestionDTO getQuestionDTO() {
        return questionDTO;
    }
    
    /**
     * Shows conversation for the given parent.
     *
     * @param questionDTO parent question's dto.
     */
    public void showConversation(QuestionDTO questionDTO) {
        this.questionDTO = questionDTO;

        CommentingServiceAsync commentingService = (CommentingServiceAsync) GWT.create(CommentingService.class);

        commentingService.getConversation(questionDTO.getEncodedKey(), new ConversationAsyncCalBack(this));
    }

    /**
     * Sets conversationGrid.
     *
     * @param conversationGrid Conversation grid.
     */
    public void setConversationGrid(Grid conversationGrid) {
        conversationGrid.getColumnFormatter().setWidth(0, "450");
        
        this.conversationScroll.clear();
        this.conversationScroll.add(conversationGrid);

        this.refreshConversationPanel();
    }

    /**
     * Refreshes conversation panel.
     */
    private void refreshConversationPanel() {
        this.conversationPanel.clear();
        this.conversationPanel.add(this.conversationScroll);
        this.conversationPanel.add(this.treeScroll);
    }

    /**
     * Sets conversation tree.
     * @param conversationTree tree of conversation.
     */
    public void setConversationTree(Tree conversationTree) {
        this.treeScroll.clear();

        this.treeScroll.add(conversationTree);

        this.refreshConversationPanel();
    }
}

/**
 * ConversationAsyncCalBack class.
 */
class ConversationAsyncCalBack implements AsyncCallback {
    /**
     * Corresponding conversation object.
     */
    private Conversation conversation;

    public ConversationAsyncCalBack(Conversation conversation) {
        this.conversation = conversation;
    }

    /**
     * On failure.
     *
     * @param throwable exception thrown by server.
     */
    public void onFailure(Throwable throwable) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * onSuccess.
     *
     * @param o Object returned from server.
     */
    public void onSuccess(Object o) {
        List<ReplyDTO> replies = (List<ReplyDTO>) o;

        Map<ReplyDTO, TreeItem> replyDTOTreeItemMap = new HashMap<ReplyDTO, TreeItem>();

        Grid grid = new Grid(replies.size(), 3);

        Tree newTree = this.initializeConversationTree(replies, replyDTOTreeItemMap);

        this.createConversationGridAndTree(replies, replyDTOTreeItemMap, grid);

        this.conversation.setConversationGrid(grid);
        this.conversation.setConversationTree(newTree);
    }

    /**
     * Creates conversation grid and tree.
     * @param replies replies received.
     * @param replyDTOTreeItemMap replyDTO to tree item map.
     * @param grid conversation grid.
     */
    private void createConversationGridAndTree(
            List<ReplyDTO> replies,
            Map<ReplyDTO, TreeItem> replyDTOTreeItemMap,
            Grid grid) {
        int index = 0;

        for (ReplyDTO reply : replies) {

            grid.setWidget(index, 0, new Label(reply.getReplyText()));

            TreeItem replyNode = new TreeItem(this.getTreeItemString(reply));

            TreeItem parentNode = replyDTOTreeItemMap.get(reply.getParent());
            
            if (parentNode != null) // null Happens if replyNode is a root's child node.
            {
                replyDTOTreeItemMap.put(reply, replyNode);
                parentNode.addItem(replyNode);
            }


            Label userLabel = new Label(reply.getCreatedBy());
            grid.setWidget(index, 1, userLabel);


            Label innerReplyLabel = new TextLink("Reply");
            innerReplyLabel.addStyleName("SimpleTextLink");
            innerReplyLabel.addClickListener(new InnerReplyClickListener(index, grid, reply.getEncodedKey()));
            grid.setWidget(index, 2, innerReplyLabel);

            grid.getCellFormatter().addStyleName(index, 0, "GridCell");
            grid.getCellFormatter().addStyleName(index, 1, "GridCell");
            grid.getCellFormatter().addStyleName(index, 2, "GridCell");

            index++;
        }
    }

    /**
     * Initializes the conversation tree.
     * @param replies list of replies received.
     * @param replyDTOTreeItemMap) ReplyDTO to tree node map.
     * @return Initializes tree.
     */
    private Tree initializeConversationTree(List<ReplyDTO> replies, Map<ReplyDTO, TreeItem> replyDTOTreeItemMap) {
        Tree newTree = new Tree();

        QuestionDTO questionDTO = this.conversation.getQuestionDTO();

        DateTimeFormat sdf = DateTimeFormat.getFormat("MMM dd");
        String rootText = questionDTO.getCreatedBy() + " " + sdf.format(questionDTO.getCreatedOn());

        TreeItem rootNode = new TreeItem(rootText);
        newTree.addItem(rootNode);

        for (ReplyDTO reply : replies) {
            if (reply.getParent() == null) {
                TreeItem rootChildNode = new TreeItem(this.getTreeItemString(reply));
                rootNode.addItem(rootChildNode);
                replyDTOTreeItemMap.put(reply, rootChildNode);
            }
        }

        return newTree;
    }

    /**
     * Gets the node string for a given comment.
     * @param reply input comment.
     * @return node string.
     */
    private String getTreeItemString(ReplyDTO reply) {
        DateTimeFormat sdf = DateTimeFormat.getFormat("MMM dd");
        return reply.getCreatedBy() + " " + sdf.format(reply.getCreatedOn());
    }

    /**
     * ReplyListener class.
     */
    private class InnerReplyClickListener implements ClickListener {

        /**
         * index of reply.
         */
        private int index;

        /**
         * Comments grid.
         */
        private Grid commentsGrid;

        /**
         * Question key.
         */
        private String parentReplyKey;

        /**
         * Initializes an instance of ReplyListener.
         *
         * @param index        index of row clicked.
         * @param commentsGrid The comments grid.
         * @param parentReplyKey  key of the question.
         */
        public InnerReplyClickListener(int index, Grid commentsGrid, String parentReplyKey) {
            this.index = index;
            this.commentsGrid = commentsGrid;
            this.parentReplyKey = parentReplyKey;
        }

        /**
         * Onclick.
         *
         * @param widget widget.
         */
        public void onClick(Widget widget) {
            Label commentLAbel = (Label) this.commentsGrid.getWidget(index, 0);
            VerticalPanel replyBox = new VerticalPanel();
            replyBox.add(commentLAbel);

            final TextArea replyArea = new TextArea();
            replyArea.setSize("300px", "100px");
            replyBox.add(replyArea);

            Button submitButton = new Button("Submit", new ClickListener() {
                public void onClick(Widget widget) {
                    CommentingServiceAsync myService = (CommentingServiceAsync) GWT.create(CommentingService.class);
                    myService.posInnertReply(
                            replyArea.getText(),
                            InnerReplyClickListener.this.parentReplyKey,
                            ConversationAsyncCalBack.this.conversation.getQuestionDTO().getEncodedKey(),
                            new AsyncCallback() {

                        public void onFailure(Throwable throwable) {
                            replyArea.setText("Failed to post Reply : " + replyArea.getText() + " Because of " + throwable);
                        }

                        public void onSuccess(Object o) {
                            replyArea.setText("Posted Reply : " + replyArea.getText());
                            replyArea.setReadOnly(true);
                        }
                    });
                }
            });

            replyBox.add(submitButton);

            this.commentsGrid.setWidget(index, 0, replyBox);
        }
    }
}

